module Admins
  class PaymentRequestsController < ControllerBase
    include ActionController::MimeResponds
    include ActionController::Live

    before_action :set_plan, only: [:index, :create, :import]
    before_action :set_payment_request, only: [:show, :update, :destroy]

    before_action :validate_plan_inactiveness, only: [:create, :import, :update, :destroy]
    before_action :validate_order_absence, only: [:update, :destroy]

    def index
      klass_ids = if params[:academic_partition_id]
        tmp = AcademicPartitionHierarchy.where(ancestor_id: params[:academic_partition_id])
        max_distance = tmp.maximum(:distance)
        tmp.where(distance: max_distance).pluck(:descendant_id)
      end

      @payment_requests = @plan.payment_requests
          .left_outer_joins(:order)
          .includes(:plan)
          .includes(order: [:charge, :payer])
          .includes(klass: [grade: [department: [:college]]])
          .paid(params[:paid] && params[:paid] == 'true')

      @payment_requests.where!(klass: klass_ids) if klass_ids
      if params[:search].present?
        @payment_requests.where!(
            'student_name like :search or orders.parent_name like :search or orders.parent_mobile like :search',
            search: "%#{params[:search]}%"
        )
      end

      respond_to do |format|
        format.json { render_page(@payment_requests.page(params[:page]).per(params[:per]), include: '**') }
        format.xlsx { export(@payment_requests) }
      end
    end

    def show
      render json: @payment_request, serializer: PaymentRequestSerializer, include: '*.*'
    end

    def create
      klass = Klass.find(params[:klass_id])
      @payment_request = @plan.payment_requests.build(student_name: params[:student_name], klass: klass)
      if @plan.save
        render json: @payment_request, status: :created, location: [:admins, @payment_request]
      else
        render json: @payment_request.errors, status: :unprocessable_entity
      end
    end

    def update
      if @payment_request.update(params.permit(:student_name))
        head :no_content
      else
        render json: @payment_request.errors, status: :unprocessable_entity
      end
    end

    def destroy
      @payment_request.destroy
    end

    def validate_plan_inactiveness
      render json: {active: ['缴费中']}, status: :unprocessable_entity if (@plan || @payment_request.plan).active?
    end

    def validate_order_absence
      render json: {order: ['已生成订单，不能编辑或删除']}, status: :unprocessable_entity if @payment_request.order
    end

    def import
      college_ids = @plan.academic_partitions.pluck(:id)
      academic_partition_ids = AcademicPartitionHierarchy.where(ancestor_id: college_ids)
                                   .pluck(:descendant_id)

      # { parent_id => {name => id} }
      partitions = AcademicPartition.where(id: academic_partition_ids)
                                .pluck(:id, :name, :parent_id)
                                .group_by{|ary| ary[-1]}
                                .map{|k, v| [ k, v.each_with_object({}) {|tuple, h| h[tuple[1]] = tuple[0]} ] }
                                .to_h

      doc = SimpleXlsxReader.open(params.require(:file).path)
      PaymentRequest.transaction do
        doc.sheets.first.data.each.with_index(2) do |row, index|
          break if row[1].blank?
          college_id = partitions[nil].try(:[], row[1])
          raise "校区不存在（第#{index}行）" unless college_id

          department_id = partitions[college_id].try(:[], row[2])
          raise "学部不存在（第#{index}行）" unless department_id

          grade_id = partitions[department_id].try(:[], row[3])
          raise "年级不存在（第#{index}行）" unless grade_id

          klass_id = partitions[grade_id].try(:[], row[4])
          raise "班级不存在（第#{index}行）" unless klass_id

          pr = @plan.payment_requests.create(academic_partition_id: klass_id, student_name: row[5])
          if pr.errors.any?
            pr.errors.each{|_, v| v << "（第#{index}行）"}
            raise ActiveRecord::RecordInvalid.new(pr)
          end
        end
      end
      head :no_content
    rescue ActiveRecord::RecordInvalid => e
      render json: e.record.errors, status: :unprocessable_entity
    rescue => e
      render json: {base: [e.message]}, status: :unprocessable_entity
    end

    private

    def set_plan
      @plan = Plan.find(params[:plan_id])
    end

    def set_payment_request
      @payment_request = PaymentRequest.find(params[:id])
    end

    def export(payment_requests)
      io = StringIO.new
      xlsx = Xlsxtream::Workbook.new(io)
      xlsx.write_worksheet 'Sheet1' do |sheet|
        sheet << %w[
          编号
          项目名
          学部
          年级
          班级
          学生姓名
          家长姓名
          家长手机
          缴费金额（元）
          缴费状态
          缴费用户
          银行卡号
          持卡人姓名
          开户银行
          渠道
          渠道用户名
          订单号
          缴费时间
        ]
        payment_requests.find_each.with_index(1) do |payment_request, index|
          sheet << [index,
                    payment_request.college.name,
                    payment_request.department.name,
                    payment_request.grade.name,
                    payment_request.klass.name,
                    payment_request.student_name,
                    payment_request.order&.parent_name,
                    payment_request.order&.parent_mobile,
                    payment_request.order&.charge&.fee,
                    (payment_request.paid? ? '已缴' : '未缴'),
                    payment_request.order&.payer&.mobile,
                    payment_request.order&.credit_card_num,
                    payment_request.order&.credit_card_holder,
                    payment_request.order&.bank,
                    payment_request.order&.payment_method,
                    payment_request.order&.payment_username,
                    payment_request.order&.order_num,
                    (payment_request.order&.completed_at? ? payment_request.order&.completed_at.strftime("%Y-%m-%d %H:%M:%S") : '')
          ]
        end
      end
      xlsx.close
      send_data io.string, disposition: %{attachment; filename="#{URI.encode(@plan.name)}.xlsx"}
    end

  end
end